package com.fintech.pangu.service;

import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenGrayReleaseRuleDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO;
import com.fintech.pangu.commons.api.BaseResponse;
import com.fintech.pangu.model.vo.GrayReleaseVO;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

/**
 * @Description:
 */
@Slf4j
public class GrayReleaseService extends AbstractService{

    public GrayReleaseService(String baseUrl, Gson gson, RestTemplate restTemplate){
        super(baseUrl,gson,restTemplate);
    }

    /**
     * 获取灰度版本
     * @return
     */
    public OpenNamespaceDTO findBranch(String appId,String env,String clusterName,String namespaceName){
        if (Strings.isNullOrEmpty(clusterName)) {
            clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
        }
        if (Strings.isNullOrEmpty(namespaceName)) {
            namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
        }

        checkNotEmpty(appId, "App id");
        checkNotEmpty(env, "Env");

        String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/branches", escapePath(env), escapePath(appId),
                escapePath(clusterName), escapePath(namespaceName));

        String resMsg = get(path);

        return gson.fromJson(resMsg, OpenNamespaceDTO.class);
    }

    /**
     * 创建灰度版本
     * @return
     */
    public OpenNamespaceDTO createBranch(String appId,String env,String clusterName,String namespaceName, String operator){
        if (Strings.isNullOrEmpty(clusterName)) {
            clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
        }
        if (Strings.isNullOrEmpty(namespaceName)) {
            namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
        }

        checkNotEmpty(appId, "App id");
        checkNotEmpty(env, "Env");
        checkNotEmpty(operator, "Operator");

        String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/branches", escapePath(env), escapePath(appId),
                escapePath(clusterName), escapePath(namespaceName));

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
        postParameters.add("operator",operator);

        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity(postParameters,httpHeaders);

        String resMsg = post(path,httpEntity);

        return gson.fromJson(resMsg, OpenNamespaceDTO.class);
    }

    /**
     * 新增灰度配置
     */
    public OpenItemDTO createItem(String appId,String env,String clusterName,String namespaceName,OpenItemDTO itemDTO){
        if (Strings.isNullOrEmpty(clusterName)) {
            clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
        }
        if (Strings.isNullOrEmpty(namespaceName)) {
            namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
        }

        checkNotEmpty(appId, "App id");
        checkNotEmpty(env, "Env");
        checkNotEmpty(itemDTO.getKey(), "Item key");
        checkNotEmpty(itemDTO.getDataChangeCreatedBy(), "Item created by");

        String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/items",
                escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName));

        HttpEntity<OpenItemDTO> httpEntity = new HttpEntity(itemDTO);

        String resMsg = post(path,httpEntity);

        return gson.fromJson(resMsg, OpenItemDTO.class);
    }

    /**
     * 新增灰度规则
     */
    public void updateBranchRules(String appId, String env, String clusterName, String namespaceName, String branchName, OpenGrayReleaseRuleDTO rules,String operator){
        if (Strings.isNullOrEmpty(clusterName)) {
            clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
        }
        if (Strings.isNullOrEmpty(namespaceName)) {
            namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
        }

        checkNotEmpty(appId, "App id");
        checkNotEmpty(env, "Env");
        checkNotEmpty(operator, "Operator");

        String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/branches/%s/rules?operator={operator}", escapePath(env), escapePath(appId),
                escapePath(clusterName), escapePath(namespaceName),escapePath(branchName));

        Map<String, Object> postParameters = new HashMap<>();
        postParameters.put("operator",operator);

        HttpEntity<OpenGrayReleaseRuleDTO> httpEntity = new HttpEntity(rules);

        put(path,httpEntity,postParameters);
    }

    /**
     * 灰度发布
     */
    public OpenReleaseDTO publishGrayRelease(String appId, String env, String clusterName, String namespaceName, String branchName, NamespaceReleaseDTO releaseDTO){
        if (Strings.isNullOrEmpty(clusterName)) {
            clusterName = ConfigConsts.CLUSTER_NAME_DEFAULT;
        }
        if (Strings.isNullOrEmpty(namespaceName)) {
            namespaceName = ConfigConsts.NAMESPACE_APPLICATION;
        }

        checkNotEmpty(appId, "App id");
        checkNotEmpty(env, "Env");

        checkNotEmpty(releaseDTO.getReleaseTitle(), "Release title");
        checkNotEmpty(releaseDTO.getReleasedBy(), "Released by");

        String path = String.format("envs/%s/apps/%s/clusters/%s/namespaces/%s/branches/%s/releases",
                escapePath(env), escapePath(appId), escapePath(clusterName), escapePath(namespaceName), escapeParam(branchName));

        HttpEntity<OpenReleaseDTO> httpEntity = new HttpEntity(releaseDTO);

        String resMsg = post(path,httpEntity);

        return gson.fromJson(resMsg, OpenReleaseDTO.class);
    }

    public BaseResponse createAndGrayRelease(GrayReleaseVO grayReleaseVO){
        try{
            // 检查是否已存在灰度版本
            OpenNamespaceDTO openNamespaceDTO = this.findBranch(grayReleaseVO.getAppId(), grayReleaseVO.getEnv(), grayReleaseVO.getClusterName(),grayReleaseVO.getNamespaceName());
            if (openNamespaceDTO == null){
                log.debug("开始创建灰度版本...start");
                // 创建灰度版本
                openNamespaceDTO = this.createBranch(grayReleaseVO.getAppId(), grayReleaseVO.getEnv(), grayReleaseVO.getClusterName(),grayReleaseVO.getNamespaceName(),grayReleaseVO.getOperator());
                log.debug("灰度版本创建成功.cluserName:[{}]",openNamespaceDTO.getClusterName());

                // 新增灰度配置
                log.debug("开始新增灰度配置...start");
                grayReleaseVO.getItem().setDataChangeCreatedBy(grayReleaseVO.getOperator());
                this.createItem(grayReleaseVO.getAppId(), grayReleaseVO.getEnv(), openNamespaceDTO.getClusterName(),grayReleaseVO.getNamespaceName(),grayReleaseVO.getItem());
                log.debug("灰度配置创建成功...end");

                // 新增灰度规则
                log.debug("开始创建灰度规则...start");
                OpenGrayReleaseRuleDTO openGrayReleaseRuleDTO = new OpenGrayReleaseRuleDTO();

                Optional.of(grayReleaseVO.getRuleItems()).ifPresent(o->o.stream().forEach(s->s.setClientAppId(grayReleaseVO.getAppId())));

                openGrayReleaseRuleDTO.setAppId(grayReleaseVO.getAppId());
                openGrayReleaseRuleDTO.setClusterName(grayReleaseVO.getClusterName());
                openGrayReleaseRuleDTO.setNamespaceName(grayReleaseVO.getNamespaceName());
                openGrayReleaseRuleDTO.setBranchName(openNamespaceDTO.getClusterName());
                openGrayReleaseRuleDTO.setRuleItems(grayReleaseVO.getRuleItems());
                this.updateBranchRules(grayReleaseVO.getAppId(), grayReleaseVO.getEnv(), grayReleaseVO.getClusterName(),grayReleaseVO.getNamespaceName(),openNamespaceDTO.getClusterName(),openGrayReleaseRuleDTO,grayReleaseVO.getOperator());
                log.debug("灰度规则创建成功...end");

                // 灰度发布
                log.debug("开始灰度发布...start");
                NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
                namespaceReleaseDTO.setEmergencyPublish(true);
                namespaceReleaseDTO.setReleasedBy(grayReleaseVO.getOperator());
                namespaceReleaseDTO.setReleaseTitle(UUID.randomUUID().toString());
                this.publishGrayRelease(grayReleaseVO.getAppId(), grayReleaseVO.getEnv(), grayReleaseVO.getClusterName(),grayReleaseVO.getNamespaceName(),openNamespaceDTO.getClusterName(),namespaceReleaseDTO);
                log.debug("灰度发布成功...end");
            } else {
                log.debug("AppID为[{}],NameSpace为[{}]的应用下已存在一个灰度版本，请核实。",openNamespaceDTO.getAppId(), openNamespaceDTO.getNamespaceName());
                BaseResponse baseResponse = new BaseResponse();
                baseResponse.setRetCode("0110");
                baseResponse.setRetDesc("AppID为["+ openNamespaceDTO.getAppId() +"],NameSpace为["+ openNamespaceDTO.getNamespaceName() +"]的应用下已存在一个灰度版本，请核实。");
                return baseResponse;
            }
        }catch (Exception e){
            log.error("灰度发布失败；[{}]",e.getMessage());
            return BaseResponse.failure();
        }
        return BaseResponse.success();
    }

}
